package lime.middleware;

/**
 * <p>Title: Lime</p>
 * <p>Description: Lime</p>
 * <p>Copyright: Copyright (c) 2002</p>
 * <p>Company: DII - DISMI</p>
 * @author Marco Mamei
 * @version 1.0
 */

import java.util.*;

import emulator.*;
import tuplespace.middleware.*;
import tuplespace.tuples.*;
import eventengine.middleware.*;

/* In a tuple space only one tuple with a given id can be in the space. On the
contrary, in a virtual tuple space, there can be more than one.
Specifically, to each tuple's id is associated another hash map mapping
from nodes to actual tuples. In this way in a virtual tuple space there can
be only one tuple with a give id AND from strigns.
*/
public class VirtualTupleSpace
{
 String spaceId;
 public HashMap repository;
 int tupleCounter = 0;

 /* this EventInterface once set enables the virtual tuple space to notify to
 the external that tuples has been inserted or removed in the space */
 private EventInterface outsideEvent = null;

 public VirtualTupleSpace(String spaceId)
 {
  this.spaceId = spaceId;
  tupleCounter = 0;
  repository = new HashMap();
 }

 /* this method allows to install a event dispatcher in charge of notifying
 what is happening in the tuple space */
 public void setOutsideEvent(EventInterface outsideEvent)
 {
  this.outsideEvent = outsideEvent;
 }

 /* this is a method called from wihtin the classe to send and event to the
 outside world. Specifically it notifyes about tuple being inserted into the
 tuple space */
 private void firePutEvent(Tuple t)
 {
  if(outsideEvent != null && t!= null)
  {
   TsTuple eventTuple = new TsTuple("<op=IN><tuple="+t.serialize()+">");   
   outsideEvent.generateEvent(eventTuple);
  }
 }

 /* this is a method called from wihtin the classe to send and event to the
 outside world. Specifically it notifyes about tuple being removed from the
 tuple space */
 private void fireRemoveEvent(Tuple t)
 {
  if(outsideEvent != null && t!= null)
  {
   TsTuple eventTuple = new TsTuple("<op=OUT><tuple="+t.serialize()+">");
   
   //System.out.println(spaceId+" "+eventTuple.serialize());
   
   outsideEvent.generateEvent(eventTuple);
  }
 }

 private String genUniqueId()
 {
  tupleCounter++;
  return new String(spaceId+"-"+tupleCounter);
 }

 /*****************************************************************************/
 /*                          LINDA INTERFACE METHODS                          */
 /*****************************************************************************/

 /* this method inserts a 'tuple' into the 'tuplespace' */
 public void write (Tuple tuple)
 {
  if(tuple.id.equalsIgnoreCase("*"))
   tuple.id = genUniqueId(); // this should never happen, since in a virtual
                             // tuple space only tuples mirrors of others
                             // with an id already stored should be inserted.

  HashMap mirrors = (HashMap)repository.get(tuple.id);
  if(mirrors == null)
  {
   mirrors = new HashMap();
   repository.put(tuple.id,mirrors);
  }
  mirrors.put(tuple.from,tuple);
  firePutEvent(tuple);
 }

 /* this method tryes to read a tuple with a given id AND from fields */
 public Tuple keykeyrd(Tuple template)
 {
  HashMap id = (HashMap)repository.get(template.id);

  if(id == null)
   return null;

  Tuple t = (Tuple)id.get(template.from);
  return t;
 }

 /* this method tryed to read all the tuple with a given id */
 public Vector keyrd (Tuple template)
 {
  HashMap hmId = (HashMap)repository.get(template.id);
  if(hmId != null)
  {
   Collection c = hmId.values();
   return new Vector(c);
  }
  return null;
 }

 /* this method reads on the basis of standard pattern matching */
 public Vector read (Tuple template)
 {
  Vector mat = new Vector();

  /* this is a vector of hash maps */
  Vector global = new Vector(repository.values());
  for(int i=0;i<global.size();i++)
  {
   HashMap idHM = (HashMap)global.get(i);
   /* this is a vector of tupels */
   Vector id = new Vector(idHM.values());
   for(int j=0;j<id.size();j++)
   {
    Tuple t = (Tuple)id.get(j);
    if(t.match(template))
     mat.add(t);
   }
  }
  return mat;
 }

 /* this method tryes to extract a tuple with a given id AND from fields */
 public Tuple keykeyextr(Tuple template)
 {
  HashMap id = (HashMap)repository.get(template.id);
  if(id != null)
  {
   Tuple t = (Tuple)id.remove(template.from);
   fireRemoveEvent(t);
   return t;
  }
  return null;
 }

 /* this method tryed to extract all the tuple with a given id */
 public Vector keyextr (Tuple template)
 {
  Vector v = (Vector)keyrd(template);
  repository.remove(template.id);
  if(v.size()>0)
   fireRemoveEvent(template); // note that this is a trick: instead of fire one
                              // event for each tuple actually extracted, I fire
                              // an event just for the template!
  return v;
 }

 /* this method extracts on the basis of standard pattern matching */
 public Vector extract (Tuple template)
 {
  Vector mat = new Vector();

  /* this is a vector of hash maps */
  Vector global = new Vector(repository.values());
  for(int i=0;i<global.size();i++)
  {
   HashMap idHM = (HashMap)global.get(i);
   /* this is a vector of tupels */
   Vector id = new Vector(idHM.values());
   for(int j=0;j<id.size();j++)
   {
    Tuple t = (Tuple)id.get(j);
    if(t.match(template))
    {
     mat.add(t);
     keykeyextr(t); // this will notify the event happening
    }
   }
  }
  return mat;
 }

 /*****************************************************************************/
 /*                            SPECIAL METHODS                                */
 /*****************************************************************************/
 public void removePeer (String peerName)
 {
  Vector mat = new Vector();
  /* this is a vector of hash maps */
  Vector global = new Vector(repository.values());
  for(int i=0;i<global.size();i++)
  {
   HashMap idHM = (HashMap)global.get(i);
   if(idHM != null)
   {
    Tuple t = (Tuple)idHM.get(peerName);
    idHM.remove(peerName);
    fireRemoveEvent(t);
   }
  }
 }

 /*****************************************************************************/
 /*                            DEBUG METHODS                                  */
 /*****************************************************************************/

 public static void main(String[] args)
 {
  VirtualTupleSpace vts = new VirtualTupleSpace("main");
  /*
  tota.tuples.hop.GradientTuple t1 = new tota.tuples.hop.GradientTuple();
  t1.content = "<content=ciao>";
  t1.id= "totaP16-1";
  t1.from= "P16";
  t1.hop = 2;

  tota.tuples.hop.GradientTuple t2 = new tota.tuples.hop.GradientTuple();
  t2.content = "<content=ciao>";
  t2.id= "totaP16-1";
  t2.from= "P17";
  t2.hop = 3;

  tota.tuples.hop.GradientTuple t3 = new tota.tuples.hop.GradientTuple();
  t3.content = "<content=ciao>";
  t3.id= "totaP16-3";
  t3.from= "P47";
  t3.hop = 5;

  vts.write(t1);
  vts.write(t2);
  vts.write(t3);

  tota.tuples.hop.GradientTuple tq = new tota.tuples.hop.GradientTuple();
  tq.content = "<content=ciao>";
  tq.id= "totaP16-1";
  tq.from= "P81";
  tq.hop = 0;
  
  Vector v = vts.keyrd(tq);
  System.out.println(v.size());
  */
 }


 public static void main2(String[] args)
 {
  int TUPLE_TYPES = 5;
  int TUPLE_FROM = 4;

  VirtualTupleSpace vts = new VirtualTupleSpace("main");

  ArrayList tuples = new ArrayList();
  for(int i=0;i<TUPLE_TYPES;i++)
  for(int j=0;j<TUPLE_FROM;j++)
  {
   Tuple t = new Tuple("<content=content"+i+"-"+j+">");
   t.id = "id"+i;
   t.from = "P"+j;
   tuples.add(t);
  }

  /* load the tuple space */
  for(int i=0;i<tuples.size();i++)
  {
   vts.write((Tuple)tuples.get(i));
  }

  Tuple t;
  Vector v;

  t = new Tuple( "<content=*>");
  v = vts.read(t);
  printVector(v);

  /*
  t = new Tuple( "<content=*>");
  t.id = "id2";
  v = vts.keyrd(t);
  printVector(v);

  vts.removePeer("P2");

  t = new Tuple( "<content=*>");
  v = vts.read(t);
  printVector(v);
  */

  t = new Tuple("<content=content0-3>");
  t.id = "*";
  v = vts.extract(t);
  printVector(v);

  t = new Tuple("<content=*>");
  v = vts.read(t);
  printVector(v);
 }

 public static void printVector(Vector v)
 {
  System.out.println("************************");
  for(int i=0;i<v.size();i++)
  {
   Tuple x = (Tuple)v.get(i);
   System.out.println(x.serialize());
  }
  System.out.println("************************");
 }

}